using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using System.Collections.Generic;

public class MultipleStencilOutlineRenderFeature : ScriptableRendererFeature
{
    [System.Serializable]
    public class OutlinePassData
    {
        public Material outlineMaterial;
        public int renderingLayerMask = 1;
        public LayerMask outlineLayer = -1;
    }

    [System.Serializable]
    public class RenderPassSettings
    {
        public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingOpaques;   
    }

    public RenderPassSettings renderSettings = new RenderPassSettings();
    public List<OutlinePassData> outlinePasses = new List<OutlinePassData>();
    private List<OutlinePass> outlinePassInstances = new List<OutlinePass>();

    public override void Create()
    {
        outlinePassInstances.Clear();
        
        for (int i = 0; i < outlinePasses.Count; i++)
        {
            var passName = $"Outline Pass {i}";
            var outlinePass = new OutlinePass(passName, renderSettings, outlinePasses[i]);
            outlinePass.renderPassEvent = renderSettings.renderPassEvent;
            outlinePassInstances.Add(outlinePass);
        }
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        foreach (var outlinePass in outlinePassInstances)
        {
            if (outlinePass.passData.outlineMaterial != null)
            {
                renderer.EnqueuePass(outlinePass);
            }
        }
    }

    protected override void Dispose(bool disposing)
    {
        foreach (var pass in outlinePassInstances)
        {
            pass?.Dispose();
        }
        outlinePassInstances.Clear();
    }
}


public class OutlinePass : ScriptableRenderPass
{
    private static readonly ShaderTagId k_ShaderTagId = new ShaderTagId("UniversalForward");
    
    private string profilerTag;
    private MultipleStencilOutlineRenderFeature.RenderPassSettings renderSettings;
    public MultipleStencilOutlineRenderFeature.OutlinePassData passData;
    private FilteringSettings filteringSettings;
    private RenderStateBlock stencilWriteStateBlock;
    private RenderStateBlock stencilTestStateBlock;
    private ProfilingSampler profilingSampler;
    
    public OutlinePass(string profilerTag, MultipleStencilOutlineRenderFeature.RenderPassSettings renderSettings, MultipleStencilOutlineRenderFeature.OutlinePassData passData)
    {
        this.profilerTag = profilerTag;
        this.renderSettings = renderSettings;
        this.passData = passData;
        
        filteringSettings = new FilteringSettings(RenderQueueRange.opaque, passData.outlineLayer);
        
        // 设置写入模板缓冲区的状态 - 使用材质中的模板值
        stencilWriteStateBlock = new RenderStateBlock(RenderStateMask.Stencil);
        stencilWriteStateBlock.stencilReference = 1;
        stencilWriteStateBlock.stencilState = new StencilState(
            true,
            1,
            255,
            CompareFunction.Always,
            StencilOp.Replace,
            StencilOp.Keep,
            StencilOp.Keep
        );
        
        // 设置测试模板缓冲区的状态
        stencilTestStateBlock = new RenderStateBlock(RenderStateMask.Stencil);
        stencilTestStateBlock.stencilReference = 1;
        stencilTestStateBlock.stencilState = new StencilState(
            true,
            1,
            255,
            CompareFunction.NotEqual,
            StencilOp.Keep,
            StencilOp.Keep,
            StencilOp.Keep
        );
        
        profilingSampler = new ProfilingSampler(profilerTag);
    }
    
    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (passData.outlineMaterial == null)
            return;
            
        CommandBuffer cmd = CommandBufferPool.Get(profilerTag);
        
        using (new ProfilingScope(cmd, profilingSampler))
        {
            // 设置渲染层掩码
            uint renderingLayerMask = (uint)(1 << passData.renderingLayerMask);
            filteringSettings.renderingLayerMask = renderingLayerMask;
            
            // 第一步 写入模板缓冲区
            context.ExecuteCommandBuffer(cmd);
            cmd.Clear();
            
            DrawingSettings normalDrawingSettings = CreateDrawingSettings(
                k_ShaderTagId, 
                ref renderingData, 
                SortingCriteria.CommonOpaque
            );
            
            // 绘制到模板缓冲区
            context.DrawRenderers(renderingData.cullResults, ref normalDrawingSettings, 
                                 ref filteringSettings, ref stencilWriteStateBlock);
            
            // 第二步 使用模板测试绘制描边
            DrawingSettings outlineDrawingSettings = CreateDrawingSettings(
                k_ShaderTagId, 
                ref renderingData, 
                SortingCriteria.CommonOpaque
            );
            outlineDrawingSettings.overrideMaterial = passData.outlineMaterial;
            outlineDrawingSettings.overrideMaterialPassIndex = 0;
            
            // 绘制描边
            context.DrawRenderers(renderingData.cullResults, ref outlineDrawingSettings, 
                                 ref filteringSettings, ref stencilTestStateBlock);
        }
        
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    public void Dispose() { }
}